package org.springblade.modules.repair.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import lombok.AllArgsConstructor;
import org.springblade.common.config.BladeOssRuleExt;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.repair.enums.ComplianceStatusEnum;
import org.springblade.modules.repair.enums.ElevatorMaintainStatusEnums;
import org.springblade.modules.repair.enums.ElevatorUseTypeEnum;
import org.springblade.modules.repair.mapper.MaintainElevatorStatisticsMapper;
import org.springblade.modules.repair.service.IMaintainElevatorStatisticsService;
import org.springblade.modules.repair.vo.ElevatorPlanScheduleDataVO;
import org.springblade.modules.repair.vo.LoseMaintainElevatorDataVO;
import org.springblade.modules.repair.vo.statistics.ps.*;
import org.springblade.modules.resource.builder.oss.OssBuilder;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 维保电梯统计
 * @Author ysq
 * @Date 2023/6/28
 **/
@AllArgsConstructor
@Service
public class MaintainElevatorStatisticsServiceImpl implements IMaintainElevatorStatisticsService {

	private final MaintainElevatorStatisticsMapper maintainElevatorStatisticsMapper;
	private final OssBuilder ossBuilder;

	@Override
	public OverviewVO planScheduleOverview() {
		OverviewVO overviewVO = new OverviewVO();
		List<ElevatorPlanScheduleDataVO> list = maintainElevatorStatisticsMapper.elevatorPlanScheduleData();

		// 已排期电梯数
		long planEleTotal = list.stream().filter(t -> t.getMaintainPlanCount() > 0).count();
		overviewVO.setPlanEleTotal(planEleTotal);
		// 从未排期电梯数
		long notPlanEleTotal = list.stream().filter(t -> t.getMaintainPlanCount() == 0).count();
		overviewVO.setNotPlanEleTotal(notPlanEleTotal);

		// 有剩余排期电梯数
		long featureScheduleEleCount = list.stream().filter(t -> t.getMaintainPlanCount() > 0 && t.getPlanId() != null).count();
		overviewVO.setFeatureScheduleEleCount(featureScheduleEleCount);
		// 有剩余排期电梯数占比
		if (planEleTotal > 0) {
			BigDecimal featureScheduleElePct = new BigDecimal(featureScheduleEleCount).divide(new BigDecimal(planEleTotal), 3, BigDecimal.ROUND_HALF_UP);
			overviewVO.setFeatureScheduleElePct(featureScheduleElePct);
		}
		if (featureScheduleEleCount > 0) {
			// 有剩余排期年审类型占比
			Map<Integer, PctItemVO> featureUseTypeMap = list.stream().filter(t -> t.getMaintainPlanCount() > 0 && t.getPlanId() != null)
				.collect(Collectors.groupingBy(t -> t.getUseType(), Collectors.collectingAndThen(Collectors.toList(), t -> {
					PctItemVO pctItemVO = new PctItemVO();
					long useTypeCount = t.stream().count();
					pctItemVO.setValue(useTypeCount);
					BigDecimal pct = new BigDecimal(useTypeCount).divide(new BigDecimal(featureScheduleEleCount), 3, BigDecimal.ROUND_HALF_UP);
					pctItemVO.setPct(pct);
					return pctItemVO;
				})));
			overviewVO.setFeatureScheduleUseType(featureUseTypeMap);

			// 有剩余排期合同类型占比
			Map<Integer, PctItemVO> featureScheduleMaintainStatusMap = list.stream().filter(t -> t.getMaintainPlanCount() > 0 && t.getPlanId() != null)
				.collect(Collectors.groupingBy(ElevatorPlanScheduleDataVO::getMaintainStatus, Collectors.collectingAndThen(Collectors.toList(), t -> {
					PctItemVO pctItemVO = new PctItemVO();
					long maintainStatusCount = t.stream().count();
					pctItemVO.setValue(maintainStatusCount);
					BigDecimal pct = new BigDecimal(maintainStatusCount).divide(new BigDecimal(featureScheduleEleCount), 3, RoundingMode.HALF_UP);
					pctItemVO.setPct(pct);
					return pctItemVO;
				})));
			overviewVO.setFeatureScheduleMaintainStatus(featureScheduleMaintainStatusMap);
		}

		// 无剩余排期电梯数
		long notFeatureScheduleEleCount = list.stream().filter(t -> t.getMaintainPlanCount() > 0 && t.getPlanId() == null).count();
		overviewVO.setNotFeatureScheduleEleCount(notFeatureScheduleEleCount);
		if (notFeatureScheduleEleCount > 0) {
			// 无剩余排期电梯数占比
			BigDecimal notFeatureScheduleElePct = new BigDecimal(notFeatureScheduleEleCount).divide(new BigDecimal(planEleTotal), 3, BigDecimal.ROUND_HALF_UP);
			overviewVO.setNotFeatureScheduleElePct(notFeatureScheduleElePct);
			// 无剩余排期年审类型占比
			Map<Integer, PctItemVO> notFeatureUseTypeMap = list.stream().filter(t -> t.getMaintainPlanCount() > 0 && t.getPlanId() == null)
				.collect(Collectors.groupingBy(t -> t.getUseType(), Collectors.collectingAndThen(Collectors.toList(), t -> {
					PctItemVO pctItemVO = new PctItemVO();
					long useTypeCount = t.stream().count();
					pctItemVO.setValue(useTypeCount);
					BigDecimal pct = new BigDecimal(useTypeCount).divide(new BigDecimal(notFeatureScheduleEleCount), 3, BigDecimal.ROUND_HALF_UP);
					pctItemVO.setPct(pct);
					return pctItemVO;
				})));
			overviewVO.setNotFeatureScheduleUseType(notFeatureUseTypeMap);

			// 无剩余排期合同类型占比
			Map<Integer, PctItemVO> nofeatureScheduleMaintainStatusMap = list.stream().filter(t -> t.getMaintainPlanCount() > 0 && t.getPlanId() == null)
				.collect(Collectors.groupingBy(ElevatorPlanScheduleDataVO::getMaintainStatus, Collectors.collectingAndThen(Collectors.toList(), t -> {
					PctItemVO pctItemVO = new PctItemVO();
					long maintainStatusCount = t.stream().count();
					pctItemVO.setValue(maintainStatusCount);
					BigDecimal pct = new BigDecimal(maintainStatusCount).divide(new BigDecimal(notFeatureScheduleEleCount), 3, RoundingMode.HALF_UP);
					pctItemVO.setPct(pct);
					return pctItemVO;
				})));
			overviewVO.setNotFeatureScheduleMaintainStatus(nofeatureScheduleMaintainStatusMap);
		}

		// 从未排期电梯占比
		if (notPlanEleTotal > 0) {
			Map<Integer, PctItemVO> notPlanEleUseTypeMap = list.stream().filter(t -> t.getMaintainPlanCount() == 0)
				.collect(Collectors.groupingBy(t -> t.getUseType(), Collectors.collectingAndThen(Collectors.toList(), t -> {
					PctItemVO pctItemVO = new PctItemVO();
					long useTypeCount = t.stream().count();
					pctItemVO.setValue(useTypeCount);
					BigDecimal pct = new BigDecimal(useTypeCount).divide(new BigDecimal(notPlanEleTotal), 3, BigDecimal.ROUND_HALF_UP);
					pctItemVO.setPct(pct);
					return pctItemVO;
				})));
			overviewVO.setNotPlanEleUseType(notPlanEleUseTypeMap);
		}

		return overviewVO;
	}

	@Override
	public ComplianceStatusVO planComplianceStatus() {
		ComplianceStatusVO complianceStatusVO = new ComplianceStatusVO();
		List<ElevatorPlanScheduleDataVO> list = maintainElevatorStatisticsMapper.elevatorPlanScheduleDataExcel().stream()
			.filter(t -> t.getUseType() == ElevatorUseTypeEnum.ANNUAL_VERIFICATION.getStatus())
			.filter(t -> t.getOverdueDate() != null).collect(Collectors.toList());
		if (Func.isNull(list)) {
			return complianceStatusVO;
		}

		// 总数
		long elevatorTotal = list.stream().count();
		// 根据合规状态分组
		Map<Integer, List<ElevatorPlanScheduleDataVO>> mapData = list.stream().collect(Collectors.groupingBy(t -> {
			/**
			 * 若年审梯，保养合规状态：
			 * 正常：今日 < 当前逾期临界日期
			 * 明天即将违规：今日 = 当前逾期临界日期
			 * 已违规：今日 > 当前逾期临界日期
			 */
			int overdueCompare= DateUtil.compare(DateUtil.date(), t.getOverdueDate(), "yyyy-MM-dd");
			if (overdueCompare < 0) {
				return ComplianceStatusEnum.NORMAL.getId();
			} else if (overdueCompare == 0) {
				return ComplianceStatusEnum.VIOLATING.getId();
			} else {
				return ComplianceStatusEnum.VIOLATED.getId();
			}
		}));

		// 饼图数据
		Map<Integer, PctItemVO> maintainStatusData = new HashMap<>(8);
		// 柱状图数据
		Map<Integer, Map<String, Long>> maintainStatusBarChartData = new HashMap<>(8);
		mapData.forEach((k,v) -> {
			PctItemVO pctItemVO = new PctItemVO();
			long itemCount = v.stream().count();
			pctItemVO.setValue(itemCount);
			if (elevatorTotal > 0) {
				BigDecimal pct = new BigDecimal(itemCount).divide(new BigDecimal(elevatorTotal), 3, BigDecimal.ROUND_HALF_UP);
				pctItemVO.setPct(pct);
			}
			maintainStatusData.put(k, pctItemVO);

			Map<String, Long> barCharMap = v.stream().collect(Collectors.groupingBy(t -> t.getDeptName() == null ? "未分组" : t.getDeptName(), Collectors.counting()));
			Map<String, Long> sortMap = new LinkedHashMap<>(8);
			// 排序
			barCharMap.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).forEachOrdered(t -> sortMap.put(t.getKey(), t.getValue()));
			maintainStatusBarChartData.put(k, sortMap);
		});

		// 补0数据
		if (maintainStatusData.size() < ComplianceStatusEnum.values().length) {
			Set<Integer> allKeys = ComplianceStatusEnum.getStatus();
			Set<Integer> keys = maintainStatusData.keySet();
			allKeys.removeAll(keys);
			for (Integer allKey : allKeys) {
				maintainStatusData.put(allKey, new PctItemVO());
			}
		}
		complianceStatusVO.setMaintainStatusData(maintainStatusData);
		complianceStatusVO.setMaintainStatusBarChartData(maintainStatusBarChartData);

		return complianceStatusVO;
	}

	@Override
	public PlanLoseVO planLose() {
		PlanLoseVO planLoseVO = new PlanLoseVO();
		List<LoseMaintainElevatorDataVO> list = maintainElevatorStatisticsMapper.planLoseList();
		// 已脱保电梯数
		long loseEleTotal = list.stream().count();
		planLoseVO.setLoseEleTotal(loseEleTotal);
		// 未清除剩余排期数
		long notDelPlanTotal = list.stream().filter(t -> t.getIsPlan() > 0).count();
		planLoseVO.setNotDelPlanTotal(notDelPlanTotal);
		// 脱保梯剩余排期清除进度占比
		if (loseEleTotal > 0) {
			BigDecimal notDelPlanPct = new BigDecimal(loseEleTotal - notDelPlanTotal).divide(new BigDecimal(loseEleTotal), 3, BigDecimal.ROUND_HALF_UP);
			planLoseVO.setNotDelPlanPct(notDelPlanPct);
		}
		return planLoseVO;
	}

	@Override
	public String planScheduleExcel(int type) throws IOException{
		String excelName = "有剩余排期电梯";
		List<ElevatorPlanScheduleDataVO> list = maintainElevatorStatisticsMapper.elevatorPlanScheduleDataExcel();
		if (type == 0) {
			list = list.stream().filter(t -> t.getMaintainPlanCount() > 0 && t.getPlanId() != null).sorted(Comparator.comparing(t -> t.getBuildingName())).collect(Collectors.toList());
		} else {
			excelName = "无剩余排期电梯";
			list = list.stream().filter(t -> t.getMaintainPlanCount() > 0 && t.getPlanId() == null).sorted(Comparator.comparing(t -> t.getBuildingName())).collect(Collectors.toList());
		}

		ExcelWriter excelWriter = null;
		excelWriter = ExcelUtil.getBigWriter();
		List<Map<String, Object>> rows = new ArrayList<>();
		for (ElevatorPlanScheduleDataVO dataVO : list) {
			Map<String, Object> rowMap = new LinkedHashMap<>(8);
			rowMap.put("项目名称", dataVO.getBuildingName());
			rowMap.put("梯号", dataVO.getElevatorAddress());
			rowMap.put("电梯年检年类型", dataVO.getUseType() == 0 ? "年审梯" : "非年审梯");
			rowMap.put("在保情况", ElevatorMaintainStatusEnums.getName(dataVO.getMaintainStatus()));
			rowMap.put("有无未来保养排期计划", type == 0 ? "有" : "无");
			rowMap.put("电梯所属维保组", Func.isBlank(dataVO.getDeptName()) ? "未分配" : dataVO.getDeptName());
			rowMap.put("维保责任人", Func.isBlank(dataVO.getRealNames()) ? "未分配" : dataVO.getRealNames());
			rows.add(rowMap);
		}

		excelWriter.setColumnWidth(0, 40);
		excelWriter.setColumnWidth(1, 40);
		excelWriter.setColumnWidth(2, 20);
		excelWriter.setColumnWidth(3, 40);
		excelWriter.setColumnWidth(4, 40);
		excelWriter.setColumnWidth(5, 40);
		excelWriter.setColumnWidth(6, 40);
		excelWriter.write(rows, true);

		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		excelWriter.flush(bos, true);
		InputStream is = new ByteArrayInputStream(bos.toByteArray());
		String str = ossBuilder.template().putFile(BladeOssRuleExt.DIV_FILE_NAME + excelName + "_" + DateUtil.format(new Date(),"yyyyMMddHHmmss")+ ".xlsx", is).getLink();
		excelWriter.close();
		is.close();

		return str;
	}

	@Override
	public String notPlanScheduleExcel() throws IOException {
		List<ElevatorPlanScheduleDataVO> list = maintainElevatorStatisticsMapper.elevatorPlanScheduleDataExcel()
			.stream().filter(t -> t.getMaintainPlanCount() == 0).sorted(Comparator.comparing(t -> t.getBuildingName())).collect(Collectors.toList());
		ExcelWriter excelWriter = null;
		excelWriter = ExcelUtil.getBigWriter();
		List<Map<String, Object>> rows = new ArrayList<>();
		for (ElevatorPlanScheduleDataVO dataVO : list) {
			Map<String, Object> rowMap = new LinkedHashMap<>(8);
			rowMap.put("项目名称", dataVO.getBuildingName());
			rowMap.put("梯号", dataVO.getElevatorAddress());
			rowMap.put("年审类型", dataVO.getUseType() == 0 ? "年审梯" : "非年审梯");
			rowMap.put("电梯所属维保组", Func.isBlank(dataVO.getDeptName()) ? "未分配" : dataVO.getDeptName());
			rowMap.put("维保责任人", Func.isBlank(dataVO.getRealNames()) ? "未分配": dataVO.getRealNames());
			rows.add(rowMap);
		}

		excelWriter.setColumnWidth(0, 40);
		excelWriter.setColumnWidth(1, 40);
		excelWriter.setColumnWidth(2, 20);
		excelWriter.setColumnWidth(3, 40);
		excelWriter.setColumnWidth(4, 40);
		excelWriter.write(rows, true);

		String excelName = "从未排期电梯";
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		excelWriter.flush(bos, true);
		InputStream is = new ByteArrayInputStream(bos.toByteArray());
		String str = ossBuilder.template().putFile(BladeOssRuleExt.DIV_FILE_NAME + excelName + "_" + DateUtil.format(new Date(),"yyyyMMddHHmmss")+ ".xlsx", is).getLink();
		excelWriter.close();
		is.close();

		return str;
	}

	@Override
	public String planComplianceStatusExcel(Integer type) throws IOException{
		List<ElevatorPlanScheduleDataVO> list = maintainElevatorStatisticsMapper.elevatorPlanScheduleDataExcel().stream()
			.filter(t -> t.getUseType() == ElevatorUseTypeEnum.ANNUAL_VERIFICATION.getStatus())
			.filter(t -> t.getOverdueDate() != null).collect(Collectors.toList());

		list = list.stream().filter(t -> {
			/**
			 * 若年审梯，保养合规状态：
			 * 正常：今日 < 当前逾期临界日期
			 * 明天即将违规：今日 = 当前逾期临界日期
			 * 已违规：今日 > 当前逾期临界日期
			 */
			int overdueCompare= DateUtil.compare(DateUtil.date(), t.getOverdueDate(), "yyyy-MM-dd");
			if (type == 1 && overdueCompare < 0) {
				return true;
			} else if (type == 2 && overdueCompare == 0) {
				return true;
			} else if (type == 3 && overdueCompare > 0) {
				return true;
			} else {
				return false;
			}
		}).map(m -> {
			if (Func.isBlank(m.getDeptName())) {
				m.setDeptName("未分配");
			}
			if (Func.isBlank(m.getRealNames())) {
				m.setRealNames("未分配");
			}
			return m;
		}).sorted(Comparator.comparing(ElevatorPlanScheduleDataVO :: getDeptName).thenComparing(ElevatorPlanScheduleDataVO :: getBuildingName)).collect(Collectors.toList());

		ExcelWriter excelWriter = null;
		excelWriter = ExcelUtil.getBigWriter();
		List<Map<String, Object>> rows = new ArrayList<>();
		for (ElevatorPlanScheduleDataVO dataVO : list) {
			Map<String, Object> rowMap = new LinkedHashMap<>(8);
			rowMap.put("项目名称", dataVO.getBuildingName());
			rowMap.put("梯号", dataVO.getElevatorAddress());
			rowMap.put("电梯年检年类型", dataVO.getUseType() == 0 ? "年审梯" : "非年审梯");
			rowMap.put("年审梯保养合规状态", type == 1 ? "正常" : type == 2 ? "明天即将违规" : "已违规");
			rowMap.put("上次维保签字日期", dataVO.getPreviousPlanDate());
			long overdueDay= DateUtil.between(dataVO.getOverdueDate(), DateUtil.date(), DateUnit.DAY, false);
			rowMap.put("违规天数", overdueDay > 0 ? String.valueOf(overdueDay) : "-");
			rowMap.put("电梯所属维保组", dataVO.getDeptName());
			rowMap.put("维保责任人", dataVO.getRealNames());
			rows.add(rowMap);
		}

		excelWriter.setColumnWidth(0, 40);
		excelWriter.setColumnWidth(1, 40);
		excelWriter.setColumnWidth(2, 20);
		excelWriter.setColumnWidth(3, 40);
		excelWriter.setColumnWidth(4, 40);
		excelWriter.setColumnWidth(5, 40);
		excelWriter.setColumnWidth(6, 40);
		excelWriter.setColumnWidth(7, 40);
		excelWriter.write(rows, true);

		String excelName;
		if (type == 1) {
			excelName = "保养正常年审梯";
		} else if (type == 2) {
			excelName = "保养即将违规年审梯";
		} else {
			excelName = "保养已违规年审梯";
		}
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		excelWriter.flush(bos, true);
		InputStream is = new ByteArrayInputStream(bos.toByteArray());
		String str = ossBuilder.template().putFile(BladeOssRuleExt.DIV_FILE_NAME + excelName + "_" + DateUtil.format(new Date(),"yyyyMMddHHmmss")+ ".xlsx", is).getLink();
		excelWriter.close();
		is.close();

		return str;
	}

	@Override
	public String planLoseExcel() throws IOException {
		List<LoseMaintainElevatorDataVO> list = maintainElevatorStatisticsMapper.planLoseList()
			.stream().filter(t -> t.getIsPlan() > 0).sorted(Comparator.comparing(t ->t.getBuildingName())).collect(Collectors.toList());
		ExcelWriter excelWriter = null;
		excelWriter = ExcelUtil.getBigWriter();
		List<Map<String, Object>> rows = new ArrayList<>();
		for (LoseMaintainElevatorDataVO dataVO : list) {
			Map<String, Object> rowMap = new LinkedHashMap<>(8);
			rowMap.put("项目名称", dataVO.getBuildingName());
			rowMap.put("梯号", dataVO.getElevatorAddress());
			rowMap.put("电梯年检年类型", dataVO.getUseType() == 0 ? "年审梯" : "非年审梯");
			rowMap.put("在保情况", "脱保");
			rowMap.put("有无未来保养排期计划", dataVO.getIsPlan() > 0 ? "有" : "无");
			rows.add(rowMap);
		}

		excelWriter.setColumnWidth(0, 40);
		excelWriter.setColumnWidth(1, 40);
		excelWriter.setColumnWidth(2, 20);
		excelWriter.setColumnWidth(3, 40);
		excelWriter.setColumnWidth(4, 40);
		excelWriter.write(rows, true);

		String excelName = "未清除剩余排期脱保梯";
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		excelWriter.flush(bos, true);
		InputStream is = new ByteArrayInputStream(bos.toByteArray());
		String str = ossBuilder.template().putFile(BladeOssRuleExt.DIV_FILE_NAME + excelName + "_" + DateUtil.format(new Date(),"yyyyMMddHHmmss")+ ".xlsx", is).getLink();
		excelWriter.close();
		is.close();

		return str;
	}
}
